package jge.datatypes.math.tests;

import static org.junit.Assert.*;
import jge.datatypes.math.JGEMatrix;
import jge.datatypes.math.JGEVector;
import jge.exceptions.InvalidMatrixDimensionsException;
import jge.exceptions.SingularMatrixException;
import junit.framework.Assert;

import org.junit.Test;

public class JGEMatrixTests {

	@Test
	public final void testJGEMatrix() {
		float[][] array = {{1, 2, 3, 4}, {1, 2, 3, 4}, {5, 6, 7, 8}, {5, 6, 7, 8}};
		try {
			JGEMatrix mat = new JGEMatrix(array);
			Assert.assertNotNull(mat);
			Assert.assertEquals(array, mat.toArray());
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
	}
	
	@Test (expected=InvalidMatrixDimensionsException.class)
	public final void testJGEMatrixWrongDimensions() throws InvalidMatrixDimensionsException {
		float[][] array = {{1, 2}, {4, 6}, {5, 6}};
		new JGEMatrix(array);
	}

	@Test
	public final void testGet() {
		try {
			JGEMatrix mat = new JGEMatrix(new float[][] {{0, 2, 5, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}});
			Assert.assertEquals(5.0f, mat.get(1, 3));
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
	}

	@Test
	public final void testSetIntIntFloat() {
		try {
			JGEMatrix mat = new JGEMatrix(new float[][] {{0, 2, 5, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}});
			Assert.assertEquals(0, mat.get(2, 1), 0);
			mat.set(2, 1, 7);
			Assert.assertEquals(7.0f, mat.get(2, 1), 0);
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
	}

	@Test
	public final void testSetFloatArrayArray() {
		try {
			float[][] array = {{1, 2, 3, 4}, {1, 2, 3, 4}, {5, 6, 7, 8}, {5, 6, 7, 8}};
			float[][] initial = {{0, 2, 5, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
			JGEMatrix mat = new JGEMatrix(initial);
			JGEMatrix expected = new JGEMatrix(array);
			
			mat.set(array);
			
			Assert.assertEquals(expected, mat);
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
	}

	@Test
	public final void testTranslateFloatFloatFloat() {
		JGEMatrix expected = null;
		try {
			expected = new JGEMatrix(new float[][] {{1, 0, 0, 5}, {0, 1, 0, 7}, {0, 0, 1, 3}, {0, 0, 0, 1}});
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		JGEMatrix mat = JGEMatrix.translation(5, 7, 3);
		
		Assert.assertEquals(expected, mat);
	}

	@Test
	public final void testTranslateJGEVector3() {
		JGEMatrix expected = null;
		try {
			expected = new JGEMatrix(new float[][] {{1, 0, 0, 6}, {0, 1, 0, 2}, {0, 0, 1, 9}, {0, 0, 0, 1}});
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		JGEVector translation = new JGEVector(6, 2, 9);
		JGEMatrix mat = JGEMatrix.translation(translation);
		
		Assert.assertEquals(expected, mat);
	}

	@Test
	public final void testRotateX() {
		JGEMatrix expected = null;
		try {
			expected = new JGEMatrix(new float[][] {{1, 0, 0, 0}, {0, (float) Math.cos(45), (float) -Math.sin(45), 0}, 
													{0, (float) Math.sin(45), (float) Math.cos(45), 0}, {0, 0, 0, 1}});
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		JGEMatrix mat = JGEMatrix.rotationX(45);
		
		Assert.assertEquals(expected, mat);
	}

	@Test
	public final void testRotateY() {
		JGEMatrix expected = null;
		try {
			expected = new JGEMatrix(new float[][] {{(float) Math.cos(45), 0, (float) Math.sin(45), 0}, {0, 1, 0, 0}, 
													{(float) -Math.sin(45), 0, (float) Math.cos(45), 0}, {0, 0, 0, 1}});
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		JGEMatrix mat = JGEMatrix.rotationY(45);
		
		Assert.assertEquals(expected, mat);
	}

	@Test
	public final void testRotateZ() {
		JGEMatrix expected = null;
		try {
			expected = new JGEMatrix(new float[][] {{(float) Math.cos(45), (float) -Math.sin(45), 0, 0}, 
													{(float) Math.sin(45), (float) Math.cos(45), 0, 0}, 
													{0, 0, 1, 0}, {0, 0, 0, 1}});
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		JGEMatrix mat = JGEMatrix.rotationZ(45);
		
		Assert.assertEquals(expected, mat);
	}

	@Test
	public final void testRotate() {
		fail("Not yet implemented"); // TODO
	}

	@Test
	public final void testScaleX() {
		JGEMatrix expected = null;
		try {
			expected = new JGEMatrix(new float[][] {{3, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}});
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		JGEMatrix mat = JGEMatrix.scaleX(3);
		
		Assert.assertEquals(expected, mat);
	}

	@Test
	public final void testScaleY() {
		JGEMatrix expected = null;
		try {
			expected = new JGEMatrix(new float[][] {{1, 0, 0, 0}, {0, 4, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}});
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		JGEMatrix mat = JGEMatrix.scaleY(4);
		
		Assert.assertEquals(expected, mat);
	}

	@Test
	public final void testScaleZ() {
		JGEMatrix expected = null;
		try {
			expected = new JGEMatrix(new float[][] {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 7, 0}, {0, 0, 0, 1}});
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		JGEMatrix mat = JGEMatrix.scaleZ(7);
		
		Assert.assertEquals(expected, mat);
	}

	@Test
	public final void testScale() {
		JGEMatrix expected = null;
		try {
			expected = new JGEMatrix(new float[][] {{3, 0, 0, 0}, {0, 4, 0, 0}, {0, 0, 7, 0}, {0, 0, 0, 1}});
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		JGEMatrix mat = JGEMatrix.scale(new JGEVector(3, 4, 7));
		
		Assert.assertEquals(expected, mat);
	}
	
	@Test
	public final void testMultiply() {
		try {
			JGEMatrix mat1 = new JGEMatrix(new float[][] {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}});
			JGEMatrix mat2 = mat1.clone();
			JGEMatrix expected = new JGEMatrix(new float[][] {{90, 100, 110, 120}, {202, 228, 254, 280}, {314, 356, 398, 440}, {426, 484, 542, 600}});
			
			mat1.multiply(mat2);
			
			Assert.assertEquals(expected, mat1);
			
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
	}

	@Test
	public final void testDeterminant() {
		JGEMatrix mat = null;
		float expected = 30;
		
		try {
			mat = new JGEMatrix(new float[][] {{1, 6, 2, 1},
											   {5, 5, 7, 8},
											   {8, 7, 3, 5},
											   {1, 2, 7, 7}});
		} catch(InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		Assert.assertEquals(expected, mat.determinant(), 0);
	}

	@Test
	public final void testInvert() {
		fail("Not yet implemented"); // TODO
	}
	
	@Test (expected=SingularMatrixException.class)
	public final void testInvertSingular() throws SingularMatrixException {
		JGEMatrix mat = null;
		
		try {
			mat = new JGEMatrix(new float[][] {{ 1,  2,  3,  4},
											   { 5,  6,  7,  8},
											   { 9, 10, 11, 12},
											   {13, 14, 15, 16}});
		} catch(InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		mat.invert();
	}

	@Test
	public final void testTranspose() {
		JGEMatrix mat = null;
		JGEMatrix expected = null;
		
		try {
			mat = new JGEMatrix(new float[][] {{ 1,  2,  3,  4}, 
											   { 5,  6,  7,  8}, 
											   { 9, 10, 11, 12}, 
											   {13, 14, 15, 16}});
			
			expected = new JGEMatrix(new float[][] {{1,  5,  9, 13}, 
													{2,  6, 10, 14}, 
													{3,  7, 11, 15}, 
													{4,  8, 12, 16}});
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		mat.transpose();
		
		Assert.assertEquals(expected, mat);
	}
	
	@Test
	public final void testEquals() {
		JGEMatrix mat1 = JGEMatrix.identity();
		JGEMatrix mat2 = JGEMatrix.identity();
		
		Assert.assertEquals(true, mat1.equals(mat2));
		Assert.assertEquals(true, mat2.equals(mat1));
	}

	@Test
	public final void testZero() {
		JGEMatrix mat = JGEMatrix.zero();
		JGEMatrix expected = null;
		try {
			expected = new JGEMatrix(new float[][] {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}});
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		Assert.assertEquals(expected, mat);
	}

	@Test
	public final void testIdentity() {
		JGEMatrix mat = JGEMatrix.identity();
		JGEMatrix expected = null;
		try {
			expected = new JGEMatrix(new float[][] {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}});
		} catch (InvalidMatrixDimensionsException e) {
			// SHOULD NOT FAIL!
			fail("SHOULD NOT FAIL!");
		}
		
		Assert.assertEquals(expected, mat);
	}

}
